package EA.testEAs;
import EA.*;
import EA.testproblems.*;
import EA.operators.selection.*;
import EA.recording.*;
import java.util.Hashtable;
import RKUjava.datastructures.RKUVector;
import RKUjava.util.RKUValuation;
import RKUjava.lang.RKUCloneable;
import Visualization.plot.*;

/** Implements a diffusion GA. It is possible to run the GA with either bit 
    representation or realvector representation.
    The following is a simple implementation of the main class.<br>
    <pre>
    import EA.*;
    import EA.testproblems.*;
    import EA.testEAs.DiffusionGA;
    
    public class diffusionga
    {
      &nbsp;&nbsp;static NumericalProblem curproblem;
      &nbsp;&nbsp;static DiffusionGA sga;
      &nbsp;&nbsp;static BatchJobScheduler BJS;
    
      &nbsp;&nbsp;public static void main(String argv[])
        &nbsp;&nbsp;{
	  &nbsp;&nbsp;&nbsp;&nbsp;if (argv.length==0){
	    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println((new DiffusionGA()).getDefaultSettings());
	    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(0);
	  &nbsp;&nbsp;&nbsp;&nbsp;}
	  &nbsp;&nbsp;&nbsp;&nbsp;curproblem = new UrsemMultimodal1();
  	  &nbsp;&nbsp;&nbsp;&nbsp;sga = new DiffusionGA(curproblem);
	  &nbsp;&nbsp;&nbsp;&nbsp;BJS = new BatchJobScheduler(sga);
	  &nbsp;&nbsp;&nbsp;&nbsp;BJS.startScheduler(argv);
	  &nbsp;&nbsp;&nbsp;&nbsp;System.exit(0);
        &nbsp;&nbsp;}
    }</pre>

    @see EA.BitNumericalGenome
    @see EA.RealNumericalGenome
*/
public class DiffusionGA implements BatchJobEA
{

  /** Representation type. (Default = 0)<br>
      0 = Realvalue vectors with RealNumericalGenome.<br>
      1 = Bitrepresented vectors with BitNumericalGenome.<br>
  */
  public int representationtype = 0;

  /** The current problem. */
  public NumericalProblem curproblem;

  /** The current framework. */
  public NumericalFramework nf;
    
  /** Create a diffusion GA.<br><b>Note:</b> Remember to set curproblem. */
  public DiffusionGA()
    {
      super();
    }

  /** Create a diffusion GA with the given problem. */
  public DiffusionGA(NumericalProblem aproblem)
    {
      super();
      curproblem = aproblem;
    }

  // **** BatchJob implementation *****************************

  /** Set the settings for the algorithm. The passed hashtable contains
      only String objects which are all related to the input file given 
      to the scheduler. 
  */
  public void setSettings(Hashtable settings)
    {
      String curstr;

      if ((curstr = (String)settings.get("representationtype"))!=null) {
	representationtype = (new Integer(curstr)).intValue();
      }

      switch (representationtype) {
      case 0:
	nf = new RealDiffusionGAFramework(curproblem);
	((RealDiffusionGAFramework)nf).theGA = this;
	break;
      case 1:
	break;
      }
      ((BatchJobEA)nf).setSettings(settings);

    };

  /** Get the default settings in the BatchJobScheduler format. This 
      method can be used to reconstruct an input file for the scheduler. 
  */
  public String getDefaultSettings()
    {
      RealDiffusionGAFramework f = new RealDiffusionGAFramework();
      StringBuffer res = new StringBuffer();

      res.append(BatchJobScheduler.getDefaultSettings());
      res.append("#################################################################\n");
      res.append("# ------------- Algorithmic settings ----------------------------\n");
      res.append("\n");
      res.append("# Print information to stdout (true/false)\n");
      res.append("verbose = "+f.verbose+"\n");
      res.append("\n");
      res.append("# Representation type\n");
      res.append("#  0 = Real valued vectors (RealNumericalGenome).\n");
      res.append("#  1 = Binary encoded vectors (BitNumericalGenome).\n");
      res.append("representationtype = "+representationtype+"\n");
      res.append("\n");
      res.append("# Probability for mutation\n");
      res.append("p_m = "+f.p_m+"\n");
      res.append("\n");
      res.append("# Probability for crossover\n");
      res.append("p_c = "+f.p_c+"\n");
      res.append("\n");
      res.append("# Maximal number of iterations\n");
      res.append("maxiterations = "+f.maxiterations+"\n");
      res.append("\n");
      res.append("# Grid width\n");
      res.append("gridwidth = "+f.gridwidth+"\n");
      res.append("\n");
      res.append("# Grid height\n");
      res.append("gridheight = "+f.gridheight+"\n");
      res.append("\n");
      res.append("# The world is wrapped on the width axis\n");
      res.append("gridwidthwrapped = "+f.gridwidthwrapped+"\n");
      res.append("\n");
      res.append("# The world is wrapped on the height axis\n");
      res.append("gridheightwrapped = "+f.gridheightwrapped+"\n");
      res.append("\n");
      res.append("# Radius of the neighborhood. Some interesting values are:\n");
      res.append("#   0 = Only the center cell. The model is then random search. \n");
      res.append("#   1 = Exactly the cells north, east, west and south. \n");
      res.append("#   1.5 = The cells that touches the center cell (8). \n");
      res.append("neighborhoodradius = "+f.neighborhoodradius+"\n");
      res.append("\n");
      res.append("# Display graph of fitness and mean for each run.\n");
      res.append("# 0 = Don't display graph.\n");
      res.append("# 1 = Display graph of fitness & mean.\n");
      res.append("displaygraph = 1\n\n");
      res.append("# Display trajectory graph of best individual's position.\n");
      res.append("# 0 = Don't display graph.\n");
      res.append("# 1 = Display graph of position.\n");
      res.append("displaybest = 1\n\n");

      return res.toString();
    };

  /** Initialize a batchjob. This is called once before any run is performed.
   */
  public void initializeBatchJobs()
    {};

  /** Finalize a batchjob. This is called once after all runs are performed.
   */
  public void finalizeBatchJobs()
    {
      ((BatchJobEA)nf).finalizeBatchJobs();
    };

  /** Initialize a run series. This is called once before the specified 
      number of runs are performed. (BJSruns in the batchfile)
  */
  public void initializeRunSeries()
    {
      ((BatchJobEA)nf).initializeRunSeries();
    };

  /** Finalize a run series. This is called once after the specified 
      number of runs are performed. (BJSruns in the batchfile)
  */
  public void finalizeRunSeries()
    {
      ((BatchJobEA)nf).finalizeRunSeries();
    };

  /** Initialize and start a single run of the algorithm. You can 
      usually just call doEA() from your framework.
  */
  public void initializeAndStartRun()
    {
      ((BatchJobEA)nf).initializeAndStartRun();
    };

  /** Finalize the single run of the algorithm. This is called immediately
      after the initializeAndStartRun(). It can be used to reset 
      measurements, increase counters, etc.
  */
  public void finalizeRun()
    {
      ((BatchJobEA)nf).finalizeRun();
    };

  /** Get the run data as text. Your implementation must return
      an array of objects that each implements the interface ConvertToText from the package
      EA.recording. You can use the classes found in EA.recording or 
      implement your own classes.
      @see EA.recording.ConvertToText
  */
  public Object[] getDataAsText()
    {
      return ((BatchJobEA)nf).getDataAsText();
    };
}

class RealDiffusionGAFramework extends RealNumericalFramework implements BatchJobEA, DataPlot2DExtraction
{
  // True if the info should be printed to stdout
  boolean verbose;
    
    // Probability for mutation
  public double p_m = 0.05;
    
  // Probability for crossover
  public double p_c = 0.9;

  // Maximal number of iterations
  public int maxiterations = 200;
    
  // Initial number of individuals
  public int initialpopsize = 100;

  // Is this problem a maximization problem.
  public boolean ismaximization = true;

  public boolean isdynamicproblem = false;

  // Width of the grid
  public int gridwidth = 10;

  // Height of the grid
  public int gridheight = 10;

  // Wrapped around the width axis
  public boolean gridwidthwrapped = false;

  // Wrapped around the height axis
  public boolean gridheightwrapped = false;

  // Neighborhood radius
  public double neighborhoodradius = 1;

  public NumericalProblem curproblem;

  public DiffusionGAIndividual[][] population;

  public DiffusionGA theGA;

  public int displaygraph = 0, displaybest = 0;

  DataPlot2D curplotter, curpeakplotter;

  private SequenceTable results1;

  private int neighborhoodindexes[][], traverseindexes[][];
  private DiffusionGAIndividual[] statpopulation;

  /** Create a new framework. */
  public RealDiffusionGAFramework()
    {
      super();
    }

  /** Create a new framework. */
  public RealDiffusionGAFramework(NumericalProblem numproblem)
    {
      super(numproblem);
      curproblem = numproblem;
      ismaximization = curproblem.ismaximization;
      curplotter = new DataPlot2D(this, 0);
      curpeakplotter = new DataPlot2D(this, 1);

      if (curproblem.dimensions==2) {
	curpeakplotter.setLowerX(curproblem.intervals[0].min);
	curpeakplotter.setUpperX(curproblem.intervals[0].max);
	curpeakplotter.setLowerY(curproblem.intervals[1].min);
	curpeakplotter.setUpperY(curproblem.intervals[1].max);
      }
    }

  public void Framework_doEA_inner()
    {
      statistics();
      recombine();
      alter();
      selection();
      evaluate();

      if (isdynamicproblem)
	((DynamicNumericalProblem)curproblem).updateProblem();
    }

  public void statistics() 
    {
      int i,j,k;
      int bestindex;
      DiffusionGAIndividual best = null;

      k = 0;
      for (i=0;i<gridheight;i++) {
	for (j=0;j<gridwidth;j++) {
	  statpopulation[k] = population[i][j];
	  k++;
	}
      }

      bestindex = PopulationMeasurements.indexOfBest(statpopulation, ismaximization);
      best = statpopulation[bestindex];

      results1.initializeRow();
      results1.updateLastRow(0,iteration);
      results1.updateLastRow(1,best.getFitness());
      results1.updateLastRow(2,best.getPos());
      results1.updateLastRow(2+curproblem.dimensions,PopulationMeasurements.calcMean(statpopulation));
      results1.updateLastRow(3+curproblem.dimensions,PopulationMeasurements.calcStandardDeviation(statpopulation));
	
      if (verbose)
	System.out.println("Bestfit="+best.getFitness());

    }

  public boolean terminate()
    {
      return iteration<maxiterations-1;
    }

  public void Framework_initialize_inner()
    {
      int i,j;
      RKUVector neighi, neighj;
      population = new DiffusionGAIndividual[gridheight][gridwidth];
      statpopulation = new DiffusionGAIndividual[gridheight*gridwidth];

      for (i = 0;i<gridheight;i++) {
	for (j = 0;j<gridwidth;j++) {
	  population[i][j] = new DiffusionGAIndividual(this, true, theGA.representationtype);
	};
      };
      evaluate();

      results1 = new SequenceTable("diffusionGA");
      results1.addColumn(new SequenceTableColumn("Generation"));
      results1.addColumn(new SequenceTableColumn("Bestfit"));
      results1.addColumns(curproblem.getSequenceTableColumns(""));
      results1.addColumn(new SequenceTableColumn("Mean"));
      results1.addColumn(new SequenceTableColumn("Std deviation"));

      // Calc neighborhood
      neighi = new RKUVector();
      neighj = new RKUVector();

      for(i = -((int)neighborhoodradius);i<=neighborhoodradius;i++) {
	for(j = -((int)neighborhoodradius);j<=neighborhoodradius;j++) {
	  if (Math.sqrt(i*i+j*j)<=neighborhoodradius) {
	    neighi.addElement(new Integer(i));
	    neighj.addElement(new Integer(j));
	  };
	};
      };

      neighborhoodindexes = new int[neighi.size()][2];
      traverseindexes = new int[neighi.size()][2];
      for (i=0;i<neighi.size();i++) {
	neighborhoodindexes[i][0] = ((Integer)neighi.elementAt(i)).intValue();
	neighborhoodindexes[i][1] = ((Integer)neighj.elementAt(i)).intValue();
      }

      if (isdynamicproblem)
	((DynamicNumericalProblem)curproblem).resetProblem();

    };

  /** Calculate the actual neighbors and place the indexes in the neighbors array.
	@param hpos The hpos of the center individual.
	@param wpos The wpos of the center individual.
	@param neighbors Where to place the indexes. If the world is not 
	wrapped in both directions then the array might contain -1 as 
	termination.
	@return The actual number of neighbors.
    */
  public int calcActualNeighborhood(int hpos, int wpos, int[][] neighbors)
    {
      int i, dest = 0;
      int curw, curh;

      if (gridheightwrapped && gridwidthwrapped) {
	for(i=0;i<neighborhoodindexes.length;i++) {
	  curh = neighborhoodindexes[i][0]+hpos;
	  curw = neighborhoodindexes[i][1]+wpos;

	  curh = (curh+gridheight)%gridheight;
	  curw = (curw+gridwidth)%gridwidth;
	  neighbors[dest][0] = curh;
	  neighbors[dest][1] = curw;
	  dest++;
	}
      }
      else if (!gridheightwrapped && gridwidthwrapped) {
	for(i=0;i<neighborhoodindexes.length;i++) {
	  curh = neighborhoodindexes[i][0]+hpos;
	  curw = neighborhoodindexes[i][1]+wpos;

	  if (curh>=0 && curh<gridheight) {
	    curw = (curw+gridwidth)%gridwidth;
	    neighbors[dest][0] = curh;
	    neighbors[dest][1] = curw;
	    dest++;
	  }
	}
      }
      else if (gridheightwrapped && !gridwidthwrapped) {
	for(i=0;i<neighborhoodindexes.length;i++) {
	  curh = neighborhoodindexes[i][0]+hpos;
	  curw = neighborhoodindexes[i][1]+wpos;
	  if (curw>=0 && curw<gridwidth) {
	    curh = (curh+gridheight)%gridheight;
	    neighbors[dest][0] = curh;
	    neighbors[dest][1] = curw;
	    dest++;
	  }
	}
      }
      else {
	for(i=0;i<neighborhoodindexes.length;i++) {
	  curh = neighborhoodindexes[i][0]+hpos;
	  curw = neighborhoodindexes[i][1]+wpos;
	  if (curw>=0 && curw<gridwidth && curh>=0 && curh<gridheight) {
	    neighbors[dest][0] = curh;
	    neighbors[dest][1] = curw;
	    dest++;
	  }
	}
      }
	
      if (dest<neighbors.length) {
	neighbors[dest][0] = -1;
	neighbors[dest][1] = -1;
      }

      return dest;
    }

  /** BETA style inner call for summarize().
     */
  public void Framework_summarize_inner()
    {
      statistics();
    }

  /** Override this in your implementation. */
  public void selection()
    {
      int i, j, k;
      boolean done;
      int besti, bestj;
      double bestfit, curfit;
      DiffusionGAIndividual[][] newpop = new DiffusionGAIndividual[gridheight][gridwidth];
	
      for (i=0;i<gridheight;i++) {
	for (j=0;j<gridwidth;j++) {
	  calcActualNeighborhood(i,j,traverseindexes);

	  done = false;
	  k=1;
	  besti = traverseindexes[0][0];
	  bestj = traverseindexes[0][1];
	  bestfit = population[besti][bestj].getFitness();
	  while (!done && k<traverseindexes.length) {
	    if (traverseindexes[k][0] == -1)
	      done = true;
	    else {
	      curfit = population[traverseindexes[k][0]][traverseindexes[k][1]].getFitness();
	      if (ismaximization && bestfit<curfit) {
		besti = traverseindexes[k][0];
		bestj = traverseindexes[k][1];
		bestfit = curfit;
	      }
	      else if (!ismaximization && bestfit>curfit) {
		besti = traverseindexes[k][0];
		bestj = traverseindexes[k][1];
		bestfit = curfit;
	      }
	      k++;
	    }
	  }
	  newpop[i][j] = (DiffusionGAIndividual)population[besti][bestj].clone();
	}
      }

      population = newpop;
    };

  /* Perform crossover on nations. */
  public void recombine()
    {
      int i, j;
      int count, par1, par2;
	
      DiffusionGAIndividual[][] newpop = new DiffusionGAIndividual[gridheight][gridwidth];
	
      for (i=0;i<gridheight;i++) {
	for (j=0;j<gridwidth;j++) {
	  if (UsefulRoutines.randomBiasedBoolean(p_c)) {
	    count = calcActualNeighborhood(i,j,traverseindexes);
	    par1 = UsefulRoutines.randomInt(count);
	    par2 = UsefulRoutines.randomInt(count);

	    newpop[i][j] = population[traverseindexes[par1][0]][traverseindexes[par1][1]].crossover(population[traverseindexes[par1][0]][traverseindexes[par1][1]], population[traverseindexes[par2][0]][traverseindexes[par2][1]]);
	  }
	  else {
	    newpop[i][j] = population[i][j];
	  }
	}
      }
      population = newpop;
    }

  /** Mutate all individuals. */
  public void alter()
    {
      int i, j;

      for (i=0;i<gridheight;i++) {
	for (j=0;j<gridwidth;j++) {
	  population[i][j].mutate(p_m);
	};
      };
    } 

  /** Evaluate all individuals. */
  public void evaluate()
    {
      double fitsum;
      int i, j;

      fitsum = 0;
      for (i=0;i<gridheight;i++) {
	for (j=0;j<gridwidth;j++) {
	  fitsum += population[i][j].getFitness();
	};
      };

    }

  // **** BatchJob implementation *****************************

    /** Set the settings for the algorithm. The passed hashtable contains
	only String objects which are all related to the input file given 
	to the scheduler. 
    */
  public void setSettings(Hashtable settings)
    {
      String curstr;

      if ((curstr = (String)settings.get("verbose"))!=null) {
	verbose = (new Boolean(curstr)).booleanValue();
      }

      if( (curstr = (String)settings.get("p_m"))!=null) {
	p_m = (new Double(curstr)).doubleValue();
      }

      if( (curstr = (String)settings.get("p_c"))!=null) {
	p_c = (new Double(curstr)).doubleValue();
      }

      if ((curstr = (String)settings.get("maxiterations"))!=null) {
	maxiterations = (new Integer(curstr)).intValue();
      }

      if ((curstr = (String)settings.get("gridheight"))!=null) {
	gridheight = (new Integer(curstr)).intValue();
      }

      if ((curstr = (String)settings.get("gridwidth"))!=null) {
	gridwidth = (new Integer(curstr)).intValue();
      }

      if ((curstr = (String)settings.get("gridheightwrapped"))!=null) {
	gridheightwrapped = (new Boolean(curstr)).booleanValue();
      }

      if ((curstr = (String)settings.get("gridwidthwrapped"))!=null) {
	gridwidthwrapped = (new Boolean(curstr)).booleanValue();
      }

      if( (curstr = (String)settings.get("neighborhoodradius"))!=null) {
	neighborhoodradius = (new Double(curstr)).doubleValue();
      }

      if ((curstr = (String)settings.get("displaygraph"))!=null) {
	displaygraph = (new Integer(curstr)).intValue();
      }

      if (curproblem.dimensions==2 && ((curstr = (String)settings.get("displaybest"))!=null)) {
	displaybest = (new Integer(curstr)).intValue();
      }

      if (curproblem instanceof DynamicNumericalProblem) {
	isdynamicproblem = true;
	((DynamicNumericalProblem)curproblem).setSettings(settings, this);
      }
      else
	isdynamicproblem = false;

    };

  /** Get the default settings in the BatchJobScheduler format. This 
	method can be used to reconstruct an input file for the scheduler. 
    */
  public String getDefaultSettings()
    {
      return "";
    };

  /** Initialize a batchjob. This is called once before any run is performed.
    */
  public void initializeBatchJobs()
    {};

    /** Finalize a batchjob. This is called once after all runs are performed.
    */
  public void finalizeBatchJobs()
    {
      curplotter.endPlot();
    };


  /** Initialize a run series. This is called once before the specified 
	number of runs are performed. (BJSruns in the batchfile)
    */
  public void initializeRunSeries()
    {};

    /** Finalize a run series. This is called once after the specified 
	number of runs are performed. (BJSruns in the batchfile)
    */
  public void finalizeRunSeries()
    {};

    /** Initialize and start a single run of the algorithm. You can 
	usually just call doEA() from your framework.
     */
  public void initializeAndStartRun()
    {
      doEA();
    };

  /** Finalize the single run of the algorithm. This is called immediately
	after the initializeAndStartRun(). It can be used to reset 
	measurements, increase counters, etc.
     */
  public void finalizeRun()
    {
      switch(displaygraph){ 
      case 0: // Do nothing
	break;
      case 1:
	curplotter.updatePlot();
	break;
      }

      switch(displaybest){ 
      case 0: // Do nothing
	break;
      case 1:
	curpeakplotter.updatePlot();
	break;
      }

    };

  /** Get the run data as text. Your implementation must return
	an array of objects that each implements the interface ConvertToText from the package
	EA.recording. You can use the classes found in EA.recording or 
	implement your own classes.
	@see EA.recording.ResultSet
	@see EA.recording.Measurement
	@see EA.recording.ConvertToText
    */
  public Object[] getDataAsText()
    {
      Object[] res = new Object[1];

      res[0] = results1;
      return res;
    };


  // ************* Plot interface **********************************

    /** Extract the data as a 2 dimensional array of objects. <br>
     <b>Note:</b> All second-indexes arrays must have equal length.
    */
  public Object[][] extract2DAsObjects(int plotternumber)
    {
      Object[][] res = new Object[2][0];
      switch (plotternumber) {
      case 0:
	res[0] = (results1.getColumn(1)).getDataAsArray();
	res[1] = (results1.getColumn(2+curproblem.dimensions)).getDataAsArray();
	break;
      case 1:
	res[0] = (results1.getColumn(2)).getDataAsArray();
	res[1] = (results1.getColumn(3)).getDataAsArray();
	break;
      }	    
      return res;
    };

  /** Extract the data as a 2 dimensional array of doubles.<br>
     <b>Note:</b> All second-indexes arrays must have equal length. */
  public double[][] extract2DAsDoubles(int plotternumber)
    {
      return null;
    };

  int[][] resultstypes = {{0,0},{0,0}};
  int[][] trajectorytypes = {{2,0},{2,0}};

  public int[][] extract2DDataTypes(int plotternumber)
    {
      switch(plotternumber)
	{
	case 0:
	  return resultstypes;
	case 1: 
	  return trajectorytypes;
	}
      return resultstypes;
    };

  /** Extract the name of a graph given an index. */
  public String extract2DName(int index, int plotternumber)
    {
      switch (plotternumber) {
      case 0:
	switch(index) {
	case 0:
	  return "Bestfit";
	case 1:
	  return "Mean";
	}
	break;
      case 1:
	return "Best pos";
      }
      return "";
    };
}

class DiffusionGAIndividual extends Individual implements RKUCloneable, RKUValuation, NumericalInspector
{

  /** Create a new DiffusionGAIndividual. 
      @param frame The framework for this individual.
      @param initialize True if you want to create a new Bit/Real-NumericalGenome.
      @param reptype The type of representation.
  */
  public DiffusionGAIndividual(NumericalFramework frame, boolean initialize, int reptype)
    {
      super(frame);

      if (initialize) {
	switch(reptype) {
	case 0:
	  genome = new RealNumericalGenome((RealNumericalFramework)frame,true);
	  break;
	case 1:
	  genome = new BitNumericalGenome((BitNumericalFramework)frame,true);
	  break;
	}
      }
    }

  public static DiffusionGAIndividual crossover(DiffusionGAIndividual individual1, DiffusionGAIndividual individual2)
    {
      DiffusionGAIndividual res;

      res = new DiffusionGAIndividual((NumericalFramework)individual1.framework, false, 0);

      if (individual1.genome instanceof RealNumericalGenome) {
	res.genome = RealNumericalGenome.crossover(individual1.genome, individual2.genome);
      }
      else if (individual1.genome instanceof BitNumericalGenome) {
	res.genome = BitNumericalGenome.crossover(individual1.genome, individual2.genome);
      };
      return res;
    };

  public double getFitness()
    {
      return ((NumericalFitness)((NumericalFramework)framework).fitnessfunction).calcFitness(((NumericalGenome)genome).getPos());
    }

  public Object clone()
    {
      DiffusionGAIndividual res;

      res = new DiffusionGAIndividual((NumericalFramework)framework, false, 0);
      res.genome = (Genome)(this.genome.clone());
      return res;
    }

  public double value()
    {
      return this.getFitness();
    }

  public double[] getPos()
    {
      return ((NumericalGenome)genome).getPos();
    }

  // Inteface-implementation: NumericalInspector
  public double[] inspectGetPos() { return getPos(); }
  public double inspectGetFitness() { return getFitness(); }
}
